#pragma once

namespace zzz{
/////////////////////////////////////////////////
//GradientX
template<typename T,class Major>
class zSpecialGradxExpression : public zMatrixBaseR<T,Major>
{
  using zMatrixBaseR<T,Major>::rows_;
  using zMatrixBaseR<T,Major>::cols_;
public:
  explicit zSpecialGradxExpression(const zMatrixBaseR<T,Major>& mat)
    :zMatrixBaseR<T,Major>(mat.rows_,mat.cols_),Mat_(mat){ZCHECK_GE(cols_, 2);}
  const T operator()(zuint r, zuint c) const
  {
    CheckRange(r, c);
    if (c==0) return Mat_(r,c+1)-Mat_(r,c);
    if (c==cols_-1) return Mat_(r,c)-Mat_(r,c-1);
    return (Mat_(r,c+1)-Mat_(r,c-1))/2;
  }
private:
  const zMatrixBaseR<T,Major> &Mat_;
};
template<typename T,class Major>
const zSpecialGradxExpression<T,Major> GradientX(const zMatrixBaseR<T,Major> &mat)
{
  return zSpecialGradxExpression<T,Major>(mat);
}

/////////////////////////////////////////////////
//GradientY
template<typename T,class Major>
class zSpecialGradyExpression : public zMatrixBaseR<T,Major>
{
  using zMatrixBaseR<T,Major>::rows_;
  using zMatrixBaseR<T,Major>::cols_;
public:
  explicit zSpecialGradyExpression(const zMatrixBaseR<T,Major>& mat)
    :zMatrixBaseR<T,Major>(mat.rows_,mat.cols_),Mat_(mat){ZCHECK_GE(rows_, 2);}
  const T operator()(zuint r, zuint c) const
  {
    CheckRange(r, c);
    if (r==0) return Mat_(r+1,c)-Mat_(r,c);
    if (r==rows_-1) return Mat_(r,c)-Mat_(r-1,c);
    return (Mat_(r+1,c)-Mat_(r-1,c))/2;
  }
private:
  const zMatrixBaseR<T,Major> &Mat_;
};
template<typename T,class Major>
const zSpecialGradyExpression<T,Major> GradientY(const zMatrixBaseR<T,Major> &mat)
{
  return zSpecialGradyExpression<T,Major>(mat);
}

//////////////////////////////////////////////////
//Lower triangle
template<typename T,class Major>
class zSpecialTrilExpression : public zMatrixBaseR<T,Major>
{
  using zMatrixBaseR<T,Major>::rows_;
  using zMatrixBaseR<T,Major>::cols_;
public:
  explicit zSpecialTrilExpression(const zMatrixBaseR<T,Major>& mat)
    :zMatrixBaseR<T,Major>(mat.rows_,mat.cols_),Mat_(mat){ZCHECK_EQ(rows_, cols_);}
  const T operator()(zuint r, zuint c) const
  {
    CheckRange(r, c);
    if (c > r) return 0;
    return Mat_(r, c);
  }
private:
  const zMatrixBaseR<T,Major> &Mat_;
};
template<typename T,class Major>
const zSpecialTrilExpression<T,Major> Tril(const zMatrixBaseR<T,Major> &mat)
{
  return zSpecialTrilExpression<T,Major>(mat);
}
}
